package skyinline.JDBC;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class J02_Departments {

	public static void main(String[] args) {
		
		//	1. JDBC 드라이버 불러오기
		//	JDBC 드라이버는 데이터베이스에 연결해야 할 필요가 있는 자바 클래스와 JDBC 드라이버 사이의
		//	계약을 정의하는 인터페이스들을 구현한 것
		//	드라이버는 jar 파일 형태로 배포
		
		//	JDBC 드라이버를 불러오는 방법에는 수동(manual)과 동적(dynamic)이 있음
		//	Class.forName( "oracle.jdbc.driver.OracleDriver" );
		//	Class.forName( DRV );
		
		//	forName 메서드는 ClassNotFoundException 예외를 던지기 떄문에
		// try - catch 블럭안에 둬야 함
		
		//	2. 데이터베이스 연결하기
		//	Connection 인터페이스는 데이터베이스 연결객체를 위한 그릇(템플릿)임
		//	연결객체를 얻으려면 DriverManager 클래스의 getConnection 메서드를 이용
		//	getConnection 메서드는 앞의 과정에서 메모리에 올려둔 드라이버를 찾고
		//	URL, USR, PWD 를 통해 데이터베이스 서버에 연결을 하고 그 결과를 Connection으로 반환
		
		//	JDBC 연결시 사용하는 URL 은 다음과 같음
		//	jdbc:프로토콜:하위이름
		//	jdbc:oracle:thin:@주소:포트:DB이름?user = 아이디 & amp; password = 비밀번호
		//	jdbc:mysql://주소/db이름?user = 아이디 & amp; password = 비밀번호
		
		//	3. SQL 질의문 생성 및 실행
		//	Statement, PreparedStatement 객체는 실행할 SQL 질의문을 의미함
		//	createStatement, preparedStatement 메서드를 이용해서 위 객체를 얻을 수 있음
		//	단, 위 메서드를 호출할 때에는 생성할 SQL 질의문을 인자로 전달해야 함
		//	생성한 객체를 실행하려면 executeQuery 나 executeUpdate 메서드를 이용함
		// executeQuery : SQL 질의문이 select인 경우 
		// executeUpdate : insert, update, delete, create, alter, drop 인 경우
		
		//	executeUpdate 의 실행결과는 질의문에 의해 변경된 레코드 수나 0을 반환
		// executeQuery 의 실행결과는 질의문에 의해 생성된 데이터를 담아두는 ResultSet 객체를 반한
		// 단, 생성된 데이터가 없는 경우 null은 아님
		
		//	Statement 에서 파생된 PreparedStatement 는 Statement 의 인기있는 대체제임
		//	SQL 문을 미리 컴파일하고 저장해두기 때문에 추후 동일한 SQL 문을 실행할 경우
		//	빠르게 처리된다는 장점이 제공
		
		//	4. 결과집합(ResultSet) 객체 다루기
		//	결과집합은 Statement, PreparedStatement 에서 반환된 데이터베이스 테이블을 의미함
		//	결과집합 객체는 현재 데이터 행을 가리키는 커서를 가지고 있음

		//	커서는 처음에는 테이블을 가리키고 있다가 next 메서드가 호출되면 첫번째 레코드로 이동하고
		//	true 또는 false 를 반환함
		//	true 가 반환되면 레코드가 존재한다는 의미
		//	false 가 반환되면 더이상 레코드가 없다는 의미
		//	while 을 이용해서 모든 레코드에 접근할 수 있음
		
		//	next 메서드로 레코드에 접근한 뒤, 각 필드 값을 가져오려면 getXxx 메서드를 사용해야 함
		//	일반적으로 필드 값을 문자열로 가져오는 getString 메서드를 많이 사용
		//	필드 값을 int 로 가져오려면 getInt 를 사용하면 됨
		
		//	getXxx 메서드에 필드이름 또는 필드 위치값을 인자로 전달해서 필드값을 가져올 수 있음
		//	getString(1)
		//	getString("department_id")
		
		//	5. JDBC 객체 닫기
		//	모든 JDBC 객체는 사용한 후 그들의 close 메서드를 호출해서 항상 닫아야 함
		//	try {
		//			... ...
		//			... ...
		//			rs.colse();
		//			pstmt.close();
		//			conn.close();
		//	} catch ( Exception e ) {
		//			e.printStackTrace();
		//	}
		//	위 코드는 완벽하지 않음
		//	만일, pstmt.close() 가 실패하는 경우 그 아래 코드는 실행되지 않기 때문임
		//	따라서, close 관련 코드는 finally 절에 작성해야 함
		//	즉, 예외가 발생하든 하지 않든 간에 무조건 실행되어야 하는 코드는 finally 절에 작성하면
		//	프로그램이 다소 완벽해 짐
		
		StringBuilder sb = new StringBuilder();
		
		final String DRV = "oracle.jdbc.driver.OracleDriver";
		final String URL = "jdbc:oracle:thin:@112.216.253.125:1521:xe";
		final String USR = "skyinline";
		final String PWD = "123456";
		
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		
		String SQL = "select * from departments";
		
		try {
			Class.forName( DRV );
			conn = DriverManager.getConnection(URL, USR, PWD);
			pstmt = conn.prepareStatement(SQL);
			rs = pstmt.executeQuery();
			
			do {
				if ( rs.next() ) {
					sb.append(String.format("%3d",rs.getInt("department_id")))
					.append(String.format("%20s\n",rs.getString("department_name")));
					// System.out.printf(String.format("%3d %20s\n", rs.getInt("department_id"), rs.getString("department_name")));
					System.out.println(sb);
				} else {
					System.err.println("데이터가 존재하지 않습니다.");
				}
			} while ( rs.next() );
		} catch (Exception e) {
			e.printStackTrace();
		} finally { 
			if ( rs != null ) try { rs.close(); rs = null; } catch (Exception e) {}
			if ( pstmt != null ) try { pstmt.close(); rs = null; } catch (Exception e) {}
			if ( conn != null ) try { conn.close(); rs = null; } catch (Exception e) {}
		}
	}
}
